package serverx

import (
	"fmt"
	"io"
	"reflect"
	"strings"

	"gitee.com/workits/pkgs/errorx"
	"gitee.com/workits/pkgs/logx"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	jsoniter "github.com/json-iterator/go"
)

// BindHandler 注册handler中的方法
func BindHandler(group *gin.RouterGroup, handlers ...any) {
	for _, handler := range handlers {
		typ := reflect.TypeOf(handler)
		val := reflect.ValueOf(handler)
		groupName := typ.Elem().Name()

		// 参数类型判断
		if val.Kind() != reflect.Ptr {
			panic("handler必须是一个指针类型" + groupName)
		}

		// 根据handler名称构造路由group
		if !strings.HasSuffix(groupName, "Handler") {
			panic("handler不符合命名规范:" + groupName)
		}
		groupName = groupName[:len(groupName)-len("Handler")]
		groupName = strings.ToLower(string(groupName[0])) + groupName[1:]
		newGroup := group.Group("/" + groupName)

		// 将handler的所有方法注册成路由
		for i := 0; i < typ.NumMethod(); i++ {
			methodName := typ.Method(i).Name
			action := strings.ToLower(string(methodName[0])) + methodName[1:]
			method := val.Method(i)

			// 参数判断
			// 第一个参数必须为context.Context，第二个参数为结构体指针（可选）
			inNum := method.Type().NumIn()
			if inNum == 0 || inNum > 2 {
				continue
			}
			if method.Type().In(0).String() != "context.Context" {
				continue
			}
			if inNum == 2 && method.Type().In(1).Kind() != reflect.Ptr {
				continue
			}

			// 返回值判断
			// 两个返回值：第一个返回值为结构体指针，第二个返回值为error
			outNum := method.Type().NumOut()
			if outNum != 2 {
				continue
			}
			if (method.Type().Out(0).Kind() != reflect.Ptr && method.Type().Out(0).Kind() != reflect.Slice) || method.Type().Out(1).String() != "error" {
				continue
			}

			// 添加路由
			newGroup.POST("/"+action, func(g *gin.Context) {
				ctx := g.Request.Context()

				/*
				 * handler中公开函数的约定：
				 * 第一个参数必须为context.Context，第二个参数为结构体指针（可选）
				 * 两个返回值：第一个返回值为结构体指针，第二个返回值为error
				 * 示例：
				 * func (c *XxxHandler) Fn1(ctx context.Context) (*RespModel, error)
				 * func (c *XxxHandler) Fn2(ctx context.Context, model *ReqModel) (*RespModel, error)
				 */

				// 参数处理
				params := make([]reflect.Value, inNum)
				params[0] = reflect.ValueOf(ctx)
				if inNum == 2 {
					// 读取request body内容
					body, err := io.ReadAll(g.Request.Body)
					if err != nil {
						logx.Errorf("%v", err, ctx)
						RenderError(g, errorx.ErrInvalidParameter)
						return
					}

					// 缺少参数
					if len(body) == 0 {
						logx.Warnf(errorx.ErrInvalidParameter.Error(), ctx)
						RenderError(g, errorx.ErrMissingParameter)
						return
					}

					// 打印参数
					logx.Infof(string(body), logx.String("type", "params"), ctx)

					// 生成模式需要对内容进行加/解密
					if gin.Mode() == gin.ReleaseMode {
						// TODO
						fmt.Println("UnimplementedException of decrypting")
					}

					// 将request body参数转换成结构体
					p := reflect.New(method.Type().In(1).Elem()).Interface()
					json := jsoniter.ConfigCompatibleWithStandardLibrary
					if err := json.Unmarshal(body, p); err != nil {
						logx.Warnf("%v", err, ctx)
						RenderError(g, errorx.ErrInvalidParameter)
						return
					}

					// 参数校验
					if err := Validator.Struct(p); err != nil {
						if _, ok := err.(*validator.InvalidValidationError); ok {
							logx.Errorf("%v", err, ctx)
							RenderError(g, errorx.ErrBadParameter)
							return
						}
						for _, err := range err.(validator.ValidationErrors) {
							logx.Warnf("%v", err, ctx)
							RenderError(g, errorx.ErrBadParameter)
							return
						}
					}

					params[1] = reflect.ValueOf(p)
				} else {
					logx.Infof("no params", logx.String("type", "params"), ctx)
				}

				// 调用handler中处理方法
				rets := method.Call(params)

				// 结果值处理
				if rets[1].Interface() != nil {
					RenderError(g, rets[1].Interface().(error))
				} else {
					RenderSuccess(g, rets[0].Interface())
				}
			})
			logx.Debugf("POST %s %s", newGroup.BasePath()+"/"+action, typ.Elem().Name()+"."+methodName)
		}
	}
}
